1   /*                        __    __  __  __    __  ___
2    *                       \  \  /  /    \  \  /  /  __/
3    *                        \  \/  /  /\  \  \/  /  /
4    *                         \____/__/  \__\____/__/.ɪᴏ
5    * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
6    */
7   package io.vavr.test;
8   
9   /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*\
10     G E N E R A T O R   C R A F T E D
11  \*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
12  
13  import static org.assertj.core.api.Assertions.assertThat;
14  
15  import io.vavr.CheckedFunction1;
16  import io.vavr.Tuple;
17  import io.vavr.collection.List;
18  import java.util.Random;
19  import org.junit.Test;
20  
21  public class PropertyTest {
22  
23      static <T> CheckedFunction1<T, Boolean> tautology() {
24          return any -> true;
25      }
26  
27      static <T> CheckedFunction1<T, Boolean> falsum() {
28          return any -> false;
29      }
30  
31      static final Arbitrary<Object> OBJECTS = Gen.of(null).arbitrary();
32  
33      @Test(expected = NullPointerException.class)
34      public void shouldThrowWhenPropertyNameIsNull() {
35          Property.def(null);
36      }
37  
38      @Test(expected = IllegalArgumentException.class)
39      public void shouldThrowWhenPropertyNameIsEmpty() {
40          Property.def("");
41      }
42  
43      // -- Property.check methods
44  
45      @Test
46      public void shouldCheckUsingDefaultConfiguration() {
47          final CheckResult result = Property.def("test").forAll(OBJECTS).suchThat(tautology()).check();
48          assertThat(result.isSatisfied()).isTrue();
49          assertThat(result.isExhausted()).isFalse();
50      }
51  
52      @Test
53      public void shouldCheckGivenSizeAndTries() {
54          final CheckResult result = Property.def("test").forAll(OBJECTS).suchThat(tautology()).check(0, 0);
55          assertThat(result.isSatisfied()).isTrue();
56          assertThat(result.isExhausted()).isTrue();
57      }
58  
59      @Test(expected = IllegalArgumentException.class)
60      public void shouldThrowOnCheckGivenNegativeTries() {
61          Property.def("test").forAll(OBJECTS).suchThat(tautology()).check(0, -1);
62      }
63  
64      @Test
65      public void shouldCheckGivenRandomAndSizeAndTries() {
66          final CheckResult result = Property.def("test").forAll(OBJECTS).suchThat(tautology()).check(new Random(), 0, 0);
67          assertThat(result.isSatisfied()).isTrue();
68          assertThat(result.isExhausted()).isTrue();
69      }
70  
71      // -- satisfaction
72  
73      @Test
74      public void shouldCheckPythagoras() {
75  
76          final Arbitrary<Double> real = n -> Gen.choose(0, (double) n).filter(d -> d > .0d);
77  
78          // (∀a,b ∈ ℝ+ ∃c ∈ ℝ+ : a²+b²=c²) ≡ (∀a,b ∈ ℝ+ : √(a²+b²) ∈ ℝ+)
79          final Checkable property = Property.def("test").forAll(real, real).suchThat((a, b) -> Math.sqrt(a * a + b * b) > .0d);
80          final CheckResult result = property.check();
81  
82          assertThat(result.isSatisfied()).isTrue();
83          assertThat(result.isExhausted()).isFalse();
84      }
85  
86      @Test
87      public void shouldCheckZipAndThenUnzipIsIdempotentForListsOfSameLength() {
88          // ∀is,ss: length(is) = length(ss) → unzip(zip(is, ss)) = (is, ss)
89          final Arbitrary<List<Integer>> ints = Arbitrary.list(size -> Gen.choose(0, size));
90          final Arbitrary<List<String>> strings = Arbitrary.list(
91                  Arbitrary.string(
92                      Gen.frequency(
93                          Tuple.of(1, Gen.choose('A', 'Z')),
94                          Tuple.of(1, Gen.choose('a', 'z')),
95                          Tuple.of(1, Gen.choose('0', '9'))
96                      )));
97          final CheckResult result = Property.def("test")
98                  .forAll(ints, strings)
99                  .suchThat((is, ss) -> is.length() == ss.length())
100                 .implies((is, ss) -> is.zip(ss).unzip(t -> t).equals(Tuple.of(is, ss)))
101                 .check();
102         assertThat(result.isSatisfied()).isTrue();
103         assertThat(result.isExhausted()).isFalse();
104     }
105 
106     // -- exhausting
107 
108     @Test
109     public void shouldRecognizeExhaustedParameters() {
110         final CheckResult result = Property.def("test").forAll(OBJECTS).suchThat(falsum()).implies(tautology()).check();
111         assertThat(result.isSatisfied()).isTrue();
112         assertThat(result.isExhausted()).isTrue();
113     }
114 
115     // -- falsification
116 
117     @Test
118     public void shouldFalsifyFalseProperty() {
119         final Arbitrary<Integer> ones = n -> random -> 1;
120         final CheckResult result = Property.def("test").forAll(ones).suchThat(one -> one == 2).check();
121         assertThat(result.isFalsified()).isTrue();
122         assertThat(result.isExhausted()).isFalse();
123         assertThat(result.count()).isEqualTo(1);
124     }
125 
126     // -- error detection
127 
128     @Test
129     public void shouldRecognizeArbitraryError() {
130         final Arbitrary<?> arbitrary = n -> { throw new RuntimeException("yay! (this is a negative test)"); };
131         final CheckResult result = Property.def("test").forAll(arbitrary).suchThat(tautology()).check();
132         assertThat(result.isErroneous()).isTrue();
133         assertThat(result.isExhausted()).isFalse();
134         assertThat(result.count()).isEqualTo(0);
135         assertThat(result.sample().isEmpty()).isTrue();
136     }
137 
138     @Test
139     public void shouldRecognizeGenError() {
140         final Arbitrary<?> arbitrary = Gen.fail("yay! (this is a negative test)").arbitrary();
141         final CheckResult result = Property.def("test").forAll(arbitrary).suchThat(tautology()).check();
142         assertThat(result.isErroneous()).isTrue();
143         assertThat(result.isExhausted()).isFalse();
144         assertThat(result.count()).isEqualTo(1);
145         assertThat(result.sample().isEmpty()).isTrue();
146     }
147 
148     @Test
149     public void shouldRecognizePropertyError() {
150         final Arbitrary<Integer> a1 = n -> random -> 1;
151         final Arbitrary<Integer> a2 = n -> random -> 2;
152         final CheckResult result = Property.def("test").forAll(a1, a2).suchThat((a, b) -> {
153             throw new RuntimeException("yay! (this is a negative test)");
154         }).check();
155         assertThat(result.isErroneous()).isTrue();
156         assertThat(result.isExhausted()).isFalse();
157         assertThat(result.count()).isEqualTo(1);
158         assertThat(result.sample().isDefined()).isTrue();
159         assertThat(result.sample().get()).isEqualTo(Tuple.of(1, 2));
160     }
161 
162     // -- Property.and tests
163 
164     @Test
165     public void shouldCheckAndCombinationWhereFirstPropertyIsTrueAndSecondPropertyIsTrue() {
166         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
167         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
168         final CheckResult result = p1.and(p2).check();
169         assertThat(result.isSatisfied()).isTrue();
170     }
171 
172     @Test
173     public void shouldCheckAndCombinationWhereFirstPropertyIsTrueAndSecondPropertyIsFalse() {
174         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
175         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
176         final CheckResult result = p1.and(p2).check();
177         assertThat(result.isSatisfied()).isFalse();
178     }
179 
180     @Test
181     public void shouldCheckAndCombinationWhereFirstPropertyIsFalseAndSecondPropertyIsTrue() {
182         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
183         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
184         final CheckResult result = p1.and(p2).check();
185         assertThat(result.isSatisfied()).isFalse();
186     }
187 
188     @Test
189     public void shouldCheckAndCombinationWhereFirstPropertyIsFalseAndSecondPropertyIsFalse() {
190         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
191         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
192         final CheckResult result = p1.and(p2).check();
193         assertThat(result.isSatisfied()).isFalse();
194     }
195 
196     // -- Property.or tests
197 
198     @Test
199     public void shouldCheckOrCombinationWhereFirstPropertyIsTrueAndSecondPropertyIsTrue() {
200         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
201         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
202         final CheckResult result = p1.or(p2).check();
203         assertThat(result.isSatisfied()).isTrue();
204     }
205 
206     @Test
207     public void shouldCheckOrCombinationWhereFirstPropertyIsTrueAndSecondPropertyIsFalse() {
208         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
209         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
210         final CheckResult result = p1.or(p2).check();
211         assertThat(result.isSatisfied()).isTrue();
212     }
213 
214     @Test
215     public void shouldCheckOrCombinationWhereFirstPropertyIsFalseAndSecondPropertyIsTrue() {
216         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
217         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(tautology());
218         final CheckResult result = p1.or(p2).check();
219         assertThat(result.isSatisfied()).isTrue();
220     }
221 
222     @Test
223     public void shouldCheckOrCombinationWhereFirstPropertyIsFalseAndSecondPropertyIsFalse() {
224         final Checkable p1 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
225         final Checkable p2 = Property.def("test").forAll(OBJECTS).suchThat(falsum());
226         final CheckResult result = p1.or(p2).check();
227         assertThat(result.isSatisfied()).isFalse();
228     }
229 }